﻿//========================================================= 
/**@file BOC_PinCmdDriver.h
 * @brief 中行指令集密码键盘驱动
 * 
 * @date 2017-02-13   19:35:09
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_BOC_PINCMDDRIVER_H_
#define _LIBZHOUYB_BOC_PINCMDDRIVER_H_
//--------------------------------------------------------- 
#include "../CommonCmdDriver.h"

#include "../../../extension/security/Key_Provider.h"
using zhou_yb::extension::security::Key_Provider;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace driver {
//--------------------------------------------------------- 
/// 中行密码键盘驱动
class BOC_PinDevAadpter : 
    public DevAdapterBehavior<IInteractiveTrans>,
    public RefObject
{
public:
    //----------------------------------------------------- 
    /// 加解密模式
    enum CryptMode
    {
        /// ECB加密
        ECB_Encrypt = 0x00,
        /// ECB解密
        ECB_Decrypt = 0x01,
        /// CBC加密
        CBC_Encrypt = 0x02,
        /// CBC解密
        CBC_Decrypt = 0x03
    };
    //----------------------------------------------------- 
    static bool IsSuccess(const ByteArray& recv)
    {
        return recv.IsEqual("\x02OK\x03");
    }
    static bool IsFailed(const ByteArray& recv)
    {
        return recv.IsEqual("\x02ER\x03");
    }
    static bool RecvByFormat(Ref<IInteractiveTrans> dev, ByteBuilder& data)
    {
        ByteBuilder tmp(64);
        size_t slen = 0;
        while(dev->Read(tmp))
        {
            slen = strlen(tmp.GetString());
            data.Append(tmp.SubArray(0, slen));

            // 最后一个包 
            if(slen < tmp.GetLength() || tmp[slen - 1] == 0x03)
                return true;
            tmp.Clear();
        }

        return false;
    }
    static size_t UnpackRecv(const ByteArray& recv, ByteBuilder& data)
    {
        return ByteConvert::Fold(recv.SubArray(1, recv.GetLength() - 2), data);
    }
    //----------------------------------------------------- 
protected:
    //----------------------------------------------------- 
    ByteBuilder _inputCache;
    //----------------------------------------------------- 
    /// 下载密钥
    bool _LoadKey(byte mkId, byte key1, byte key2, byte key3, const ByteArray& key_8)
    {
        LOGGER(_log << "MkId:<" << static_cast<int>(mkId) << ">\n";
        _log << "Key1:<" << static_cast<int>(key1) << ">\n";
        _log << "Key2:<" << static_cast<int>(key2) << ">\n";
        _log << "Key3:<" << static_cast<int>(key3) << ">\n";
        _log << "Key:<";_log.WriteStream(key_8) << ">\n");

        ASSERT_FuncErrInfo(key_8.GetLength() >= 8, DeviceError::ArgLengthErr, "密钥长度错误");

        ByteBuilder cmd(8);
        DevCommand::FromAscii("1B 63 31 34", cmd);

        ByteBuilder tmp(8);
        tmp += mkId;
        tmp += key1;
        tmp += key2;
        tmp += key3;

        tmp.Append(key_8.SubArray(0, 8));
        ByteConvert::Expand(tmp, cmd, 0x30);

        cmd += static_cast<byte>(0x0D);
        cmd += static_cast<byte>(0x0A);

        ASSERT_FuncErr(_pDev->Write(cmd), DeviceError::SendErr);

        ByteBuilder recv(8);
        ASSERT_FuncErr(RecvByFormat(_pDev, recv), DeviceError::RecvFormatErr);
        ASSERT_FuncErr(IsSuccess(recv), DeviceError::DevReturnErr);
        return true;
    }
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /**
     * @brief 获取密码键盘按键
     * @date 2017-02-14 13:52
     * 
     * @param [out] key 获取到的按键
     */
    bool WaitKey(byte& key)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();

        if(_inputCache.GetLength() < 1)
        {
            if(!_pDev->Read(_inputCache))
                return false;
        }
        key = _inputCache[0];
        _inputCache.RemoveFront(1);
        LOGGER(_log << "Key:<" << _hex(key) << ">\n");
        return _logRetValue(true);
    }
    /// 清空输入状态
    bool Clear()
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        bool bRet = _pDev->Write(DevCommand::FromAscii("1B 63 31 32 0D 0A"));
        ASSERT_FuncErrRet(bRet, DeviceError::SendErr);
        return _logRetValue(true);
    }
    /// 复位
    bool Reset()
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        bool bRet = _pDev->Write(DevCommand::FromAscii("1B 63 31 30 0D 0A"));
        ASSERT_FuncErrRet(bRet, DeviceError::SendErr);
        return _logRetValue(true);
    }
    /**
     * @brief 明文下载密钥
     * @date 2017-02-13 20:57
     * 
     * @param [in] keyId 密钥索引号
     * @param [in] key_8 8字节密钥 
     */
    bool LoadKey(byte keyId, const ByteArray& key_8)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        LOGGER(_log << "KeyID:<" << static_cast<int>(keyId) << ">\n";
        _log << "Key:<";_log.WriteStream(key_8) << ">\n");

        const size_t KEY_LENGTH = 8;
        ASSERT_FuncErrRet(key_8.GetLength() >= KEY_LENGTH, DeviceError::ArgLengthErr);

        ByteBuilder cmd(8);
        ByteBuilder tmp(8);
        DevCommand::FromAscii("1B 63 31 33", cmd);
        tmp += keyId;
        tmp += key_8.SubArray(0, KEY_LENGTH);
        ByteConvert::Expand(tmp, cmd, 0x30);

        cmd += static_cast<byte>(0x0D);
        cmd += static_cast<byte>(0x0A);

        ASSERT_FuncErrRet(_pDev->Write(cmd), DeviceError::SendErr);

        ByteBuilder recv(8);
        ASSERT_FuncErrRet(RecvByFormat(_pDev, recv), DeviceError::RecvFormatErr);
        ASSERT_FuncErrRet(IsSuccess(recv), DeviceError::DevReturnErr);
        return _logRetValue(true);
    }
    /**
     * @brief 密文下载密钥
     * @date 2017-02-14 15:30
     * 
     * @param [in] mkId 需要下载的密钥索引
     * @param [in] dkId 解密密钥索引
     * @param [in] key_8 密钥密文
     */
    bool LoadEncryptKey(byte mkId, byte dkId, const ByteArray& key_8)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        return _logRetValue(_LoadKey(mkId, dkId, 100, 100, key_8));
    }
    /**
     * @brief 密文下载密钥
     * @date 2017-02-13 21:10
     * 
     * @param [in] mkId 需要下载的密钥索引
     * @param [in] key1 密钥1
     * @param [in] key2 密钥2
     * @param [in] key3 密钥3
     * @param [in] key_8 : hex 8字节密钥
     */
    bool LoadEncryptKey(byte mkId, byte key1, byte key2, byte key3, const ByteArray& key_8)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        return _logRetValue(_LoadKey(mkId, key1, key2, key3, key_8));
    }
    // 取消输入
    bool Cancel()
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        return _logRetValue(_pDev->Write(DevCommand::FromAscii("83")));
    }
    /**
     * @brief 加解密数据
     * @date 2017-02-14 15:47
     * 
     * @param [in] mode 加解密模式
     * @param [in] fk 密钥1
     * @param [in] sk 密钥2
     * @param [in] tk 密钥3
     * @param [in] ivIndex IV索引
     * - 索引:
     *  - [0,64] 指定的密钥
     *  - 100 设备保存的IV
     *  - 127 外部传入的IV 
     * .
     * @param [in] iv_8 IV值
     * @param [in] data_8 需要加密的数据
     * @param [out] result_8 加密结果
     */
    bool Crypt(CryptMode mode, byte fk, byte sk, byte tk, byte ivIndex, const ByteArray& iv_8, const ByteArray& data_8, ByteBuilder& result_8)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        LOGGER(_log << "Mode:<" << static_cast<int>(mode) << ">\n";
        _log << "FKey:<" << static_cast<int>(fk) << ">\n";
        _log << "SKey:<" << static_cast<int>(sk) << ">\n";
        _log << "TKey:<" << static_cast<int>(tk) << ">\n");

        ByteBuilder cmd(8);
        DevCommand::FromAscii("1B 63 31 35", cmd);

        ByteBuilder tmp(8);
        tmp += _itobyte(mode);
        tmp += fk;
        tmp += sk;
        tmp += tk;
        tmp += ivIndex;

        ByteBuilder iv(iv_8.SubArray(0, 8));
        ByteConvert::Fill(iv, 8, true);
        tmp += iv;

        ByteBuilder data(data_8.SubArray(0, 8));
        ByteConvert::Fill(data, 8, true);
        tmp += data;

        ByteConvert::Expand(tmp, cmd, 0x30);

        cmd += static_cast<byte>(0x0D);
        cmd += static_cast<byte>(0x0A);

        ASSERT_FuncErrRet(_pDev->Write(cmd), DeviceError::SendErr);

        ByteBuilder recv(8);
        ASSERT_FuncErrRet(RecvByFormat(_pDev, recv), DeviceError::RecvFormatErr);
        ASSERT_FuncErrRet(!IsFailed(recv), DeviceError::DevReturnErr);

        data.Clear();
        UnpackRecv(recv, data);
        LOGGER(_log << "Result:<";_log.WriteStream(data) << ">\n");
        result_8 += data;
        return _logRetValue(true);
    }
    /// 设置密码长度
    bool SetPinLength(uint maxlen)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        LOGGER(_log << "MaxLen:<" << maxlen << ">\n");

        ByteBuilder cmd(8);
        cmd += 0x1B;
        cmd += 'N';
        cmd.Format("%02d", maxlen);
        ASSERT_FuncErrInfoRet(_pDev->Write(cmd), DeviceError::SendErr, "设置密码长度失败");
        ASSERT_FuncErrRet(_pDev->Read(cmd), DeviceError::RecvErr);
        return _logRetValue(true);
    }
    /**
     * @brief 输入密码(后续使用WaitKey获取按键,GetPassword获取密码)
     * @date 2017-02-14 16:00
     */
    bool WaitPassword()
    {
        LOG_FUNC_NAME();
        ASSERT_Device();

        ByteBuilder cmd(8);
        cmd += 0x1B;
        cmd += "c17";
        cmd += 0x0D;
        cmd += 0x0A;

        ASSERT_FuncErrRet(_pDev->Write(cmd), DeviceError::SendErr);
        return _logRetValue(true);
    }
    /// 开始输入
    bool WaitInput(bool isReVioce)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        return _logRetValue(_pDev->Write(DevCommand::FromAscii(isReVioce ? "82" : "81")));
    }
    /**
     * @brief 获取Pinblock
     * @date 2017-02-14 16:24
     * 
     * @param [in] fk 密钥1
     * @param [in] sk 密钥2
     * @param [in] tk 密钥3
     * @param [in] pad 后补字节
     * @param [in] format 格式
     * - 格式:
     *  - 0 IBM3624 
     *  - 1 ANSI
     *  - 2 ISO0
     *  - 3 ISO1
     *  - 4 ECI1
     *  - 5 ECT2 / VISA
     * .
     * @param [in] iv IV值
     * @param [out] result 获取到的PIN
     */
    bool GetPassword(byte fk, byte sk, byte tk, byte pad, byte format, const ByteArray& iv, ByteBuilder& result)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        LOGGER(_log << "FKey:<" << static_cast<int>(fk) << ">\n";
        _log << "SKey:<" << static_cast<int>(sk) << ">\n";
        _log << "TKey:<" << static_cast<int>(tk) << ">\n";
        _log << "Pad:<" << _hex_num(pad) << ">\n";
        _log << "Format:<" << static_cast<int>(format) << ">\n";
        _log << "Xor:<";_log.WriteStream(iv) << ">\n");

        ByteBuilder cmd(8);
        DevCommand::FromAscii("1B 63 31 38", cmd);

        ByteBuilder tmp(8);
        tmp += fk;
        tmp += sk;
        tmp += tk;
        tmp += pad;
        tmp += format;
        tmp += _itobyte(iv.GetLength());
        tmp += iv;
        ByteConvert::Expand(tmp, cmd, 0x30);
        cmd += 0x0D;
        cmd += 0x0A;
        ASSERT_FuncErrRet(_pDev->Write(cmd), DeviceError::SendErr);

        ByteBuilder recv(8);
        ASSERT_FuncErrRet(RecvByFormat(_pDev, recv), DeviceError::RecvFormatErr);
        ASSERT_FuncErrRet(!IsFailed(recv), DeviceError::DevReturnErr);

        tmp.Clear();
        UnpackRecv(recv, tmp);
        LOGGER(_log << "Pinblock:<";_log.WriteStream(tmp) << ">\n");
        result += tmp;
        return _logRetValue(true);
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
/// 中行密码键盘应用层驱动
class BOC_PinCmdDriver : 
    public CommonCmdDriver, 
    public BaseDevAdapterBehavior<IInteractiveTrans>
{
public:
    /// 将按键转换为对应的字符串描述
    static void FormatInput(byte key, ByteBuilder& input)
    {
        switch(key)
        {
        case 0x0D:
            input += "Enter";
            break;
            // 00
        case 0x4F:
            input += "00";
            break;
            // .
        case 0x2E:
            input += ".";
            break;
            // cancel
        case 0x1B:
            input += "Cancel";
            break;
            // correct
        case 0x08:
            input += "Correct";
            break;
        case 0x2A:
        case 0x4E:
            input += "*";
            break;
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '0':
            input += key;
            break;
        default:
            break;
        }
    }
protected:
    BOC_PinDevAadpter _pin;
public:
    BOC_PinCmdDriver() : CommonCmdDriver() 
    {
        _errGetter.Add(_pin);
        _logSetter.select(_pin);

        _RegisteAdapterCMD(BOC_PinCmdDriver);
        _RegisteCMD(BOC_PinCmdDriver, LoadKey);
        _RegisteCMD(BOC_PinCmdDriver, GetKCV);
        _RegisteCMD(BOC_PinCmdDriver, LoadEncryptKey);
        _RegisteCMD(BOC_PinCmdDriver, Crypt);
        _RegisteCMD(BOC_PinCmdDriver, GetMAC);
        _RegisteCMD(BOC_PinCmdDriver, WaitPinblock);
        _RegisteCMD(BOC_PinCmdDriver, GetPinblock);
        _RegisteCMD(BOC_PinCmdDriver, WaitInput);
        _RegisteCMD(BOC_PinCmdDriver, WaitKey);
        _RegisteCMD(BOC_PinCmdDriver, Input);
        _RegisteCMD(BOC_PinCmdDriver, Cancel);
    }
    LC_CMD_ADAPTER_INVOKE(IInteractiveTrans, _pin);
    /**
     * @brief 明文下载主密钥
     * @date 2017-02-14 16:43
     * 
     * @param [in] KeyIndex : uint 需要更新的密钥索引
     * @param [in] Key : hex 需要更新的密钥(16位密钥)
     *
     * @retval KCV : hex 密钥的KCV值
     */
    LC_CMD_METHOD(LoadKey)
    {
        uint keyIndex = arg["KeyIndex"].To<uint>();
        string sKey;
        if(!arg.Get("Key", sKey))
        {
            _logErr(DeviceError::ArgErr, "缺少参数[Key]");
            return false;
        }
        ByteBuilder key(16);
        DevCommand::FromAscii(sKey.c_str(), key);
        ByteConvert::Fill(key, 16, true);

        // 左边密钥
        ASSERT_FuncErrRet(_pin.LoadKey(2 * keyIndex, key.SubArray(0, 8)), DeviceError::OperatorErr);
        // 右边密钥
        ASSERT_FuncErrRet(_pin.LoadKey(2 * keyIndex + 1, key.SubArray(8)), DeviceError::OperatorErr);

        // 计算KCV
        ByteBuilder kcv(8);
        Key_Provider::KCV(key, kcv);
        rlt.Put("KCV", ArgConvert::ToString(kcv));
        return true;
    }
    /**
     * @brief 计算KCV
     * @date 2017-02-14 20:42
     * 
     * @param [in] KeyIndex : uint 计算KCV的密钥索引
     * 
     * @retval KCV : hex 密钥计算出来的KCV
     */
    LC_CMD_METHOD(GetKCV)
    {
        uint keyIndex = arg["KeyIndex"].To<uint>();
        byte fk = _itobyte(2 * keyIndex);
        byte sk = _itobyte(2 * keyIndex + 1);
        byte tk = fk;
        byte IV_BUFF[8] = { 0 };
        ByteArray iv(IV_BUFF, 8);
        ByteArray data(IV_BUFF, 8);
        ByteBuilder kcv(8);
        bool bRet = _pin.Crypt(BOC_PinDevAadpter::ECB_Encrypt, fk, sk, tk, 127, iv, data, kcv);
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);

        rlt.Put("KCV", ArgConvert::ToString(kcv));
        return true;
    }
    /**
     * @brief 密文下载密钥
     * @date 2017-02-14 16:47
     * 
     * @param [in] MkIndex : uint 解密密钥索引
     * @param [in] WkIndex : uint 需要更新的密钥索引
     * @param [in] Key : hex 密钥密文(16位密钥)
     */
    LC_CMD_METHOD(LoadEncryptKey)
    {
        uint mkIndex = arg["MkIndex"].To<uint>();
        uint wkIndex = arg["WkIndex"].To<uint>();
        string sKey;
        if(!arg.Get("Key", sKey))
        {
            _logErr(DeviceError::ArgErr, "缺少参数[Key]");
            return false;
        }
        ByteBuilder key(16);
        DevCommand::FromAscii(sKey.c_str(), key);
        ByteConvert::Fill(key, 16, true);
        // 左边半部分
        byte mk1 = _itobyte(2 * mkIndex);
        byte mk2 = _itobyte(2 * mkIndex + 1);
        byte wk1 = _itobyte(2 * wkIndex);
        byte wk2 = _itobyte(2 * wkIndex + 1);

        bool bRet = _pin.LoadEncryptKey(wk1, mk1, mk2, mk1, key.SubArray(0, 8));
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);
        bRet = _pin.LoadEncryptKey(wk2, mk1, mk2, mk1, key.SubArray(8));
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);
        return true;
    }
    /**
     * @brief 加解密数据
     * @date 2017-02-14 16:50
     * 
     * @param [in] Mode : string 加解密模式
     * - 模式
     *  - E,Enc,Encrypt 加密
     *  - D,Dec,Decrypt 解密
     * .
     * @param [in] KeyIndex : uint 加密密钥索引
     * @param [in] Data : hex 需要加密的数据
     * @param [in] IV : hex 使用外部传入的IV(8字节)
     * @param [in] IVIndex : uint 使用对应索引的密钥作为IV
     * @warning 存在IV或者IVIndex时表示使用CBC加解密方式,否则为ECB
     * 
     * @retval Data : hex 加密后的数据
     * @retval Length : uint 加密后的数据长度
     */
    LC_CMD_METHOD(Crypt)
    {
        uint keyIndex = arg["KeyIndex"].To<uint>();
        string sData;
        if(!arg.Get("Data", sData))
        {
            _logErr(DeviceError::ArgErr, "缺少参数[Data]");
            return false;
        }
        bool isCBC = false;
        string sIV;
        ByteBuilder iv(16);
        iv.Append(0x00, 8);

        byte ivIndex = 127;
        if(arg.Get("IV", sIV))
        {
            iv.Clear();
            DevCommand::FromAscii(sIV.c_str(), iv);
            ByteConvert::Fill(iv, 8, true);
            isCBC = true;
        }
        string sIvIndex;
        if(arg.Get("IVIndex", sIvIndex))
        {
            ivIndex = _itobyte(ArgConvert::FromString<uint>(sIvIndex));
            isCBC = true;
        }
        BOC_PinDevAadpter::CryptMode mode = BOC_PinDevAadpter::ECB_Encrypt;
        string sMode = arg["Mode"].To<string>();
        if(StringConvert::StartWith(sMode.c_str(), "E", true))
        {
            mode = isCBC ? BOC_PinDevAadpter::CBC_Encrypt : BOC_PinDevAadpter::ECB_Encrypt;
        }
        else if(StringConvert::StartWith(sMode.c_str(), "D", true))
        {
            mode = isCBC ? BOC_PinDevAadpter::CBC_Decrypt : BOC_PinDevAadpter::ECB_Decrypt;
        }
        else
        {
            _logErr(DeviceError::ArgFormatErr, "Mode 错误");
            return false;
        }

        ByteBuilder data(16);
        ByteBuilder recv(16);
        DevCommand::FromAscii(sData.c_str(), data);

        byte fk = _itobyte(2 * keyIndex);
        byte sk = _itobyte(2 * keyIndex + 1);
        byte tk = fk;
        bool bRet = false;
        size_t len = 0;
        for(size_t i = 0;i < data.GetLength();i += 8)
        {
            // 按照8字节为一个数据块加密
            bRet = _pin.Crypt(mode, fk, sk, tk, ivIndex, iv, data.SubArray(i, 8), recv);
            ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);

            len += 8;
        }
        rlt.Put("DATA", ArgConvert::ToString(recv));
        rlt.Put("Length", ArgConvert::ToString(len));
        return true;
    }
    /**
     * @brief 计算MAC
     * @date 2017-02-14 16:52
     * 
     * @param [in] KeyIndex 计算MAC时使用的密钥索引
     * @param [in] Pad : byte [default:0xFF] 后补字节
     * @param [in] IV : hex 向量(可选)
     * @param [in] DATA : hex 计算MAC的数据源
     * 
     * @retval MAC : hex 获取出来的MAC值
     */
    LC_CMD_METHOD(GetMAC)
    {
        uint keyIndex = arg["KeyIndex"].To<uint>();
        byte pad = arg["Pad"].To<byte>(0xFF);
        string sIV = arg["IV"].To<string>();
        string sData = arg["DATA"].To<string>();
        ByteBuilder data(16);
        DevCommand::FromAscii(sData.c_str(), data);

        byte IV_BUFF[8] = { 0 };
        ByteArray iv(IV_BUFF, 8);
        
        // 将数据以8字节分组做异或
        ByteBuilder tmpInput(8);
        ByteBuilder tmpIV(8);
        DevCommand::FromAscii(sIV.c_str(), tmpIV);
        ByteConvert::Fill(tmpIV, 8, true);

        size_t times = data.GetLength() / 8;
        size_t lastlen = data.GetLength() % 8;
        bool bRet = true;
        byte fk = _itobyte(2 * keyIndex);
        byte sk = _itobyte(2 * keyIndex + 1);
        byte tk = fk;
        ByteArray dataArray;
        byte dataBuff[8] = { 0 };
        for(size_t i = 0;i < times; ++i)
        {
            dataArray = data.SubArray(8*i, 8);
            if(i == times)
            {
                if(lastlen < 1)
                    break;
                memset(dataBuff, pad, 8);
                memcpy(dataBuff, dataArray.GetBuffer(), lastlen);
                dataArray = ByteArray(dataBuff, 8);
            }
            tmpInput.Clear();
            for(int j = 0;j < DES_BLOCK_SIZE; ++j)
                tmpInput += static_cast<byte>(dataArray[j] ^ tmpIV[j]);
            tmpIV.Clear();
            bRet = _pin.Crypt(BOC_PinDevAadpter::ECB_Encrypt, fk, 100, 100, 127, iv, tmpInput, tmpIV);
            ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);
        }
        tmpInput.Clear();
        bRet = _pin.Crypt(BOC_PinDevAadpter::ECB_Decrypt, sk, 100, 100, 127, iv, tmpIV, tmpInput);
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);
        tmpIV.Clear();
        bRet = _pin.Crypt(BOC_PinDevAadpter::ECB_Encrypt, fk, 100, 100, 127, iv, tmpInput, tmpIV);
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);

        rlt.Put("MAC", ArgConvert::ToString(tmpIV));
        return true;
    }
    /**
     * @brief 等待输入密码(输入后使用GetPinblock获取密码)
     * @date 2017-02-16 20:54
     * 
     * @param [in] Length : uint 期望输入的密码长度
     */
    LC_CMD_METHOD(WaitPinblock)
    {
        string slen;
        if(arg.Get("Length", slen))
        {
            uint len = ArgConvert::FromString<uint>(slen);
            ASSERT_FuncErrInfo(_pin.SetPinLength(len), DeviceError::OperatorErr, "设置密码输入长度失败");
        }
        return _pin.WaitPassword();
    }
    /**
     * @brief 获取Pinblock
     * @date 2017-02-14 16:59
     * 
     * @param [in] Accno : string 主帐号信息
     * @param [in] KeyIndex : byte 主密钥索引
     * @param [in] Pad : byte [default:0xFF] 后补字节
     * @param [in] Format : uint 格式
     * - 格式:
     *  - 0 IBM3624
     *  - 1 ANSI
     *  - 2 ISO0
     *  - 3 ISO1
     *  - 4 ECI1
     *  - 5 ECT2 / VISA
     * .
     * @retval PinBlock : hex 获取到的PINBLOCK数据
     */
    LC_CMD_METHOD(GetPinblock)
    {
        string sAccno = arg["Accno"].To<string>();
        uint keyIndex = arg["KeyIndex"].To<uint>();
        byte pad = arg["Pad"].To<byte>(0xFF);
        byte format = _itobyte(arg["Format"].To<uint>());

        ByteBuilder iv(8);
        ByteBuilder cardNo(8);
        Key_Provider::FormatAnsiAccno(sAccno.c_str(), cardNo);
        ByteConvert::FromAscii(cardNo, iv);

        byte fk = _itobyte(2 * keyIndex);
        byte sk = _itobyte(2 * keyIndex + 1);
        byte tk = fk;

        ByteBuilder pin(8);
        bool bRet = _pin.GetPassword(fk, sk, tk, pad, format, iv, pin);
        ASSERT_FuncErrRet(bRet, DeviceError::OperatorErr);

        rlt.Put("Pinblock", ArgConvert::ToString(pin));
        return true;
    }
    /**
     * @brief 等待输入密码
     * @date 2017-02-14 23:08
     * 
     * @param [in] IsReVoice : bool [default:false] 是否需要语音提示再次输入密码
     */
    LC_CMD_METHOD(WaitInput)
    {
        bool isReVoice = arg["IsReVoice"].To<bool>(false);
        return _pin.WaitInput(isReVoice);
    }
    /**
     * @brief 获取密码键盘按键输入
     * @date 2017-02-14 23:33
     * 
     * @retval KeyCode : byte 按键码
     * @retval Key : string 按键描述
     */
    LC_CMD_METHOD(WaitKey)
    {
        byte key = 0x00;
        if(!_pin.WaitKey(key))
            return false;
        ByteBuilder input(8);
        FormatInput(key, input);
        rlt.Put("Key", input.GetString());
        rlt.Put("KeyCode", _hex(key));
        return true;
    }
    /**
     * @brief 
     * @date 2017-02-16 21:20
     * 
     * @param [in] Length : uint [default:0] 期望输入的密码长度
     * @warning 0 表示直到输入确认键盘后才返回
     * 
     * @param [in] IsAutoEnd : bool [default:true] 是否长度满足后自动返回
     * @param [in] IsFallback : bool [default:true] 是否更正键退格,否则为全清
     * 
     * @retval PWD : string 输入的密码(有可能为星号)
     */
    LC_CMD_METHOD(Input)
    {
        uint usrlen = arg["Length"].To<uint>(0);
        bool isAutoEnd = arg["IsAutoEnd"].To<bool>(true);
        bool isFallback = arg["IsFallback"].To<bool>(true);

        byte key = 0x00;
        ByteBuilder input(8);
        while(_pin.WaitKey(key))
        {
            switch(key)
            {
                // 确认键
            case 0x0D:
                isAutoEnd = true;
                break;
                // 取消键
            case 0x1B:
                _logErr(DeviceError::OperatorInterruptErr, "用户取消输入");
                return false;
                // 更正键
            case 0x08:
                if(isFallback)
                    input.RemoveTail();
                else
                    input.Clear();
                break;
            default:
                input += static_cast<byte>(key);
                break;
            }
            LOGGER(_log << "Input[" << input.GetLength() << "]:<" << input.GetString() << ">\n");
            if(isAutoEnd && input.GetLength() >= usrlen)
            {
                rlt.Put("PWD", input.GetString());
                return true;
            }
        }
        return false;
    }
    /// 取消输入状态
    LC_CMD_METHOD(Cancel)
    {
        return _pin.Cancel();
    }
};
//--------------------------------------------------------- 
} // nemespace driver
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_BOC_PINCMDDRIVER_H_
//========================================================= 